package logger

import (
	"fmt"
	"os"
	"sync"

	"github.com/robfig/cron"
)

type ILoggers interface {
	Alloc() ILogger
	Release(ILogger)
}

type stdout struct {
	*os.File
	version int
	index   int
}

func New(threads int, feed string, level Level, flag int) (ILoggers, error) {
	jobs := cron.New()
	kit := &loggers{feed: feed, last: build(feed), level: level, flag: flag, pool: new(pool)}
	kit.pool.Init(threads, kit.alloc)
	err := jobs.AddFunc(spec(feed), kit.reset)
	if err != nil {
		return nil, err
	}
	jobs.Start()
	return kit, nil
}

type loggers struct {
	mux     sync.Mutex
	feed    string
	last    string
	level   Level
	index   int
	version int
	flag    int
	pool    *pool
}

func (l *loggers) Alloc() ILogger {
	kit := l.pool.Alloc().(*logger)
	if w, ok := kit.kit.Writer().(*stdout); ok {
		l.mux.Lock()
		if w.version != l.version || w.File == nil {
			if w.File != nil {
				w.Close()
				w.File = nil
			}
			w.version = l.version
			file := fmt.Sprintf(l.last, w.index)
			l.mux.Unlock()
			out, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
			if err != nil {
				out = os.Stderr
			}
			w.File = out
		} else {
			l.mux.Unlock()
		}
	}
	return kit
}

func (l *loggers) Release(x ILogger) {
	if kit, ok := x.(*logger); ok {
		if w, ok := kit.kit.Writer().(*stdout); ok {
			l.mux.Lock()
			if w.version != l.version {
				w.Close()
				w.File = nil
			}
			l.mux.Unlock()
		}
	}
	l.pool.Release(x)
}

func (l *loggers) alloc() interface{} {
	out := new(stdout)
	l.mux.Lock()
	out.index = l.index
	out.version = l.version
	l.index++
	l.mux.Unlock()
	return NewLogger(out, l.level, l.flag)
}

func (l *loggers) reset() {
	file := build(l.feed)
	l.mux.Lock()
	if file == l.last {
		l.mux.Unlock()
		return
	}
	l.last = file
	l.version++
	l.mux.Unlock()
}
